/* Mesquite source code (Rhetenor package).  Copyright 1997 and onward E. Dyreson and W. Maddison. 


Disclaimer:  The Mesquite source code is lengthy and we are few.  There are no doubt inefficiencies and goofs in this code. 
The commenting leaves much to be desired. Please approach this source code with the spirit of helping out.
Perhaps with your help we can be more than a few, and make Mesquite better.

Mesquite is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY.
Mesquite's web site is http://mesquiteproject.org

This source code and its compiled class files are free and modifiable under the terms of 
GNU Lesser General Public License.  (http://www.gnu.org/copyleft/lesser.html)
*/
package mesquite.rhetenor.CharsFromMatrices;
/*~~  */

import java.util.*;
import java.awt.*;

import mesquite.lib.*;
import mesquite.lib.characters.*;
import mesquite.lib.duties.*;
import mesquite.rhetenor.lib.*;

/* ======================================================================== */
/* This is a character source used privately by Rhetenor for ShowCharLoadings.  Note that it is not user chooseable. */
public class CharsFromMatrices extends CharsFromMatrixSource implements Selectionable {
	public void getEmployeeNeeds(){  //This gets called on startup to harvest information; override this and inside, call registerEmployeeNeed
		EmployeeNeed e = registerEmployeeNeed(MatrixSourceCoord.class, getName() + "  needs a source of matrices.",
		"The source of matrices is selected initially");
	}
	/*.................................................................................................................*/
	int currentChar=0;
	MatrixSourceCoord matrixSource;
	MCharactersDistribution matrix;
	Vector indirectListeners;
	/*.................................................................................................................*/
	public boolean startJob(String arguments, Object condition, boolean hiredByName) {
		indirectListeners = new Vector();
  	 	return true; 
  	 }
	/*.................................................................................................................*/
	public boolean startJob(Object condition) {
		indirectListeners = new Vector();
  	 	return false; 
  	 }
  	 
	/*.................................................................................................................*/
	/** Generated by an employee who quit.  The MesquiteModule should act accordingly. */
 	public void employeeQuit(MesquiteModule employee) {
 		if (employee == matrixSource)  // character source quit and none rehired automatically
 			iQuit();
	}
   	public boolean getUserChooseable(){
   		return false;
   	}
   	/** Called to provoke any necessary initialization.  This helps prevent the module's intialization queries to the user from
   	happening at inopportune times (e.g., while a long chart calculation is in mid-progress)*/
   	public void initialize(Taxa taxa){
   	}
	/*.................................................................................................................*/
 	public void setMatrixSource(MatrixSourceCoord source){
 		matrixSource = source;
 	}
	/*.................................................................................................................*/
   	public CharacterDistribution getCharacter(Taxa taxa, int ic) {
   		currentChar =ic;
		matrix = matrixSource.getCurrentMatrix(taxa); //TODO: don't do this every time!
		if (matrix == null)
			return null;
		
		//
  		CharacterDistribution dist =  matrix.getCharacterDistribution(ic);
  		if (dist == null)
  			return null;
  		if (dist instanceof CharacterStates)
  			((CharacterStates)dist).setParentCharacter(ic);
  		return dist;
   	}
	/*.................................................................................................................*/
   	public int getNumberOfCharacters(Taxa taxa) {
   		if (matrix==null) {
   			if (matrixSource==null)
   				return 0;
			matrix = matrixSource.getCurrentMatrix(taxa); //TODO: don't do this every time!
   		}
  		return countIncludedCharacters(matrix); //matrix.getNumChars();
   	}
   	public boolean usesTree(){
   		if (matrixSource==null)
   			return false;
   		else
   			return matrixSource.usesTree();
   	}
	/*.................................................................................................................*/
   	public CharacterDistribution getCharacter(Tree tree, int ic) {
   		currentChar =ic;
		matrix = matrixSource.getCurrentMatrix(tree); //TODO: don't do this every time!
  		CharacterDistribution dist =  matrix.getCharacterDistribution(ic);
  		if (dist instanceof CharacterStates)
  			((CharacterStates)dist).setParentCharacter(ic);
  		return dist;
   	}
	/*.................................................................................................................*/
   	public int getNumberOfCharacters(Tree tree) {
   		if (matrix==null) {
   			if (matrixSource==null)
   				return 0;
			matrix = matrixSource.getCurrentMatrix(tree); //TODO: don't do this every time!
   		}
  		return countIncludedCharacters(matrix); //matrix.getNumChars();
   	}
   	
  	/** zzzzzzzzzzzz*/
	/*.................................................................................................................*/
	CharacterPartition colorSet;
	public void prepareItemColors(Taxa taxa){
   		if (matrix==null) {
   			if (matrixSource==null)
   				return;
			matrix = matrixSource.getCurrentMatrix(taxa); //TODO: don't do this every time!
   		}
   		if (matrix ==null)
   			return;
   		CharacterData data = matrix.getParentData();
   		if (data ==null)
   			return;
		if (taxa==null || data == null || data.getTaxa()!=taxa)
			colorSet=null;
		else
			colorSet = (CharacterPartition)data.getCurrentSpecsSet(CharacterPartition.class); 
	}
	boolean respectExclusion = true;
	public int countIncludedCharacters(MCharactersDistribution matrix){
		if (matrix == null)
			return 0;
   		CharacterData data = matrix.getParentData();
		if (data==null)
			return 0;
		if (!respectExclusion)
			return data.getNumChars();
		CharInclusionSet incl = (CharInclusionSet)data.getCurrentSpecsSet(CharInclusionSet.class);
		int numChars = data.getNumChars();
		if (incl==null)
			return numChars;
		int count = 0;
		for (int i=0; i<numChars; i++)
			if (incl.isBitOn(i))
				count++;
		return count;

	}
	public int countIncludedCharacters(CharacterData data){
		if (data==null)
			return 0;
		if (!respectExclusion)
			return data.getNumChars();
		CharInclusionSet incl = (CharInclusionSet)data.getCurrentSpecsSet(CharInclusionSet.class);
		int numChars = data.getNumChars();
		if (incl==null)
			return numChars;
		int count = 0;
		for (int i=0; i<numChars; i++)
			if (incl.isBitOn(i))
				count++;
		return count;

	}
	public int findIncludedCharacter(CharacterData data, int ic){
		if (!respectExclusion)
			return ic;
		CharInclusionSet incl = (CharInclusionSet)data.getCurrentSpecsSet(CharInclusionSet.class);
		int numChars = data.getNumChars();
		if (incl==null)
			return ic;
		int count = 0;
		for (int i=0; i<numChars; i++){
			if (incl.isBitOn(i)){
				if (count == ic)
					return i;
				count++;
			}
		}
		return -1;

	}
	public Color getItemColor(Taxa taxa, int ic){
		if (taxa ==null || matrix == null || colorSet == null)
			return null;
		
		if (colorSet!=null){
			int whichCharacter = ic;
	   		CharacterData data = matrix.getParentData();
	   		if (data ==null)
	   			return null;
			if (respectExclusion)
				whichCharacter = findIncludedCharacter(data, ic);
			CharactersGroup mi = (CharactersGroup)colorSet.getProperty(whichCharacter);
			if (mi!=null && mi.getColor()!=null) {
				return mi.getColor();
			}
			return null;
		}
		return null;
	}

	public Selectionable getSelectionable(){
		return this;  //data or this
	}
	
	CharacterData getData(){
		if (matrix == null)
			return null;
		
		return matrix.getParentData();
	}
	/** Sets whether or not the part is selected */
	public void setSelected(int part, boolean select){
		CharacterData data = getData();
		if (data == null)
			return;
		int ic = findIncludedCharacter(data, part);
		if (ic>=0)
			data.setSelected(ic, select);
	}

	/** Returns whether the part is selected */
	public boolean getSelected(int part){
		CharacterData data = getData();
		if (data == null)
			return false;
		int ic = findIncludedCharacter(data, part);
		if (ic>=0)
			return data.getSelected(ic);
		return false;
	}

	/** Deselects all parts */
	public void deselectAll(){
		CharacterData data = getData();
		if (data == null)
			return;
		data.deselectAll();
	}

	/** Selects all parts */
	public void selectAll(){
		CharacterData data = getData();
		if (data == null)
			return;
		data.selectAll();
	}



	/** Returns whether there are any selected parts */
	public boolean anySelected(){
		CharacterData data = getData();
		if (data == null)
			return false;
		if (!respectExclusion)
			return data.anySelected();
		CharInclusionSet incl = (CharInclusionSet)data.getCurrentSpecsSet(CharInclusionSet.class);
		int numChars = data.getNumChars();
		if (incl==null)
			return data.anySelected();
		for (int i=0; i<numChars; i++)
			if (incl.isBitOn(i)  && data.getSelected(i))
				return true;
		return false;
	}

	/** Returns number of selected parts */
	public int numberSelected(){
		CharacterData data = getData();
		if (data == null)
			return 0;

		CharInclusionSet incl = (CharInclusionSet)data.getCurrentSpecsSet(CharInclusionSet.class);
		int numChars = data.getNumChars();
		if (incl==null)
			return numChars;
		int count = 0;
		for (int i=0; i<numChars; i++)
			if (incl.isBitOn(i) && data.getSelected(i)) //included and selected
				count++;
		return count;
	}

	/** Returns number of parts that can be selected*/
	public int getNumberOfSelectableParts(){
		CharacterData data = getData();
		if (data == null)
			return 0;
		return countIncludedCharacters(data);
	}
	/*.................................................................................................................*/
	/*.................................................................................................................*/
	/** lists listeners of element*/
	public boolean amIListening(MesquiteListener obj) {
		CharacterData data = getData();
		if (data == null)
			return false;
		return data.amIListening(obj);
	}
	/** lists listeners of element*/
	public void listListeners() {
		CharacterData data = getData();
		if (data == null)
			return;
		data.listListeners();
	}
	/** notifies listeners that element has changed*/
	public void notifyListeners(Object caller, Notification notification){
		CharacterData data = getData();
		if (data == null)
			return;
		data.notifyListeners(caller, notification);
	}
	/** adds a listener to notify if the element changes*/
	public void addListener(MesquiteListener listener){
		CharacterData data = getData();
		if (data == null)
			return;
		if (indirectListeners.indexOf(listener)<0)
			indirectListeners.addElement(listener);
		data.addListener(listener);
	}
	/** adds a listener to notify if the element changes; add to start of listener vector so it will be notified early*/
	public void addListenerHighPriority(MesquiteListener listener){
		CharacterData data = getData();
		if (data == null)
			return;
		if (indirectListeners.indexOf(listener)<0)
			indirectListeners.addElement(listener);
		data.addListenerHighPriority(listener);
	}
	/** removes a listener*/
	public void removeListener(MesquiteListener listener){
		CharacterData data = getData();
		if (data == null)
			return;
		indirectListeners.removeElement(listener);
		data.removeListener(listener);
	}
	/** Increments the suppression of listener notification*/
	public void incrementNotifySuppress(){
		CharacterData data = getData();
		if (data == null)
			return;
		data.incrementNotifySuppress();
	}
	/** Decrements the suppression of listener notification*/
	public void decrementNotifySuppress(){
		CharacterData data = getData();
		if (data == null)
			return;
		data.decrementNotifySuppress();
	}

   	/** returns the name of character ic*/
   	public String getCharacterName(Taxa taxa, int ic){
   		return "Character #" + CharacterStates.toExternal(ic) ;
   	}
	/*.................................................................................................................*/
  	 public CompatibilityTest getCompatibilityTest() {
  	 	return new CharacterStateTest();
  	 }
	/*.................................................................................................................*/
    	 public String getName() {
		return "Characters from Matrix Source";
   	 }
	/*.................................................................................................................*/
 	/** returns an explanation of what the module does.*/
 	public String getExplanation() {
 		return "Supplies characters from source of matrices." ;
   	 }
	/*.................................................................................................................*/
    	 public boolean isPrerelease() {
		return false;
   	 }
   	 
}

